// Copyright (c) 2025 The WebRTC project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS.  All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.

#include "modules/video_coding/codecs/av1/libaom_speed_config_factory.h"

#include <algorithm>
#include <optional>

#include "api/video_codecs/encoder_speed_controller.h"
#include "api/video_codecs/video_codec.h"

namespace webrtc {

namespace {

constexpr int kNumLevels = 15;
EncoderSpeedController::Config::SpeedLevel kAllLevels[kNumLevels] = {
    {.speeds = {5, 5, 6, 6}, .min_qp = 31},
    {.speeds = {5, 6, 7, 7}, .min_qp = 30},
    {.speeds = {5, 6, 8, 10}, .min_qp = 30},
    {.speeds = {5, 6, 9, 11}, .min_qp = 29},
    {.speeds = {5, 7, 7, 7}, .min_qp = 29},
    {.speeds = {7, 7, 8, 8}, .min_qp = 28},
    {.speeds = {7, 7, 8, 9}, .min_qp = 28},
    {.speeds = {7, 7, 10, 10}, .min_qp = 28},
    {.speeds = {7, 7, 10, 11}, .min_qp = 27},
    {.speeds = {7, 7, 11, 11}, .min_qp = 26},
    {.speeds = {7, 8, 9, 9}, .min_qp = 26},
    {.speeds = {7, 9, 9, 11}, .min_qp = 25},
    {.speeds = {8, 9, 10, 11}, .min_qp = 25},
    {.speeds = {9, 10, 11, 11}, .min_qp = std::nullopt},
    {.speeds = {10, 11, 11, 11}, .min_qp = std::nullopt}};

bool HasSameSpeeds(const EncoderSpeedController::Config::SpeedLevel& a,
                   const EncoderSpeedController::Config::SpeedLevel& b,
                   int num_temporal_layers) {
  if (a.speeds[0] != b.speeds[0] || a.speeds[1] != b.speeds[1]) {
    // Keyframe or base layer speed differs.
    return false;
  }
  if (num_temporal_layers > 1 && a.speeds[3] != b.speeds[3]) {
    // Upper (non-reference) layer speed differs.
    return false;
  }
  // Middle temporal layer (intermedia class).
  return a.speeds[2] == b.speeds[2];
}

void AddSpeedLevels(int num_levels,
                    int num_temporal_layers,
                    EncoderSpeedController::Config& config) {
  // Add up to `num_levels` speeds - but ignore levels that have identical
  // speeds when `num_temporal_layers` is used (e.g. same base-layer speed for
  // single-layer).
  config.speed_levels.reserve(num_levels);
  for (int i = kNumLevels - 1; i >= kNumLevels - num_levels; --i) {
    if (i == kNumLevels - 1 ||
        !HasSameSpeeds(kAllLevels[i], config.speed_levels.back(),
                       num_temporal_layers)) {
      config.speed_levels.push_back(kAllLevels[i]);
    }
  }

  std::reverse(config.speed_levels.begin(), config.speed_levels.end());
}

}  // namespace

LibaomSpeedConfigFactory::LibaomSpeedConfigFactory(
    VideoCodecComplexity complexity,
    VideoCodecMode mode)
    : complexity_(complexity), mode_(mode) {}

EncoderSpeedController::Config LibaomSpeedConfigFactory::GetSpeedConfig(
    int width,
    int height,
    int num_temporal_layers) const {
  EncoderSpeedController::Config config;
  int num_levels = 0;
  switch (complexity_) {
    case VideoCodecComplexity::kComplexityLow:
      // Level 9x10x11x11 and up.
      num_levels = 2;
      break;
    case VideoCodecComplexity::kComplexityNormal:
      // Level 8x9x10x11 and up.
      num_levels = 3;
      break;
    case VideoCodecComplexity::kComplexityHigh:
      // Level 7x7x10x10 and up.
      num_levels = 8;
      break;
    case VideoCodecComplexity::kComplexityHigher:
      // Level 5x6x8x10 and up (< 720p, 5x7x7x7 otherwise)
      if (width * height < 1280 * 720) {  // Corrected condition
        num_levels = 12;
      } else {
        num_levels = 10;
      }
      break;
    case VideoCodecComplexity::kComplexityMax:
      // All levels.
      num_levels = kNumLevels;
      break;
  }

  if (mode_ == VideoCodecMode::kScreensharing) {
    num_levels = std::max(1, num_levels - 1);
  }

  AddSpeedLevels(num_levels, num_temporal_layers, config);

  // Don't cap speed based on resolution - only adjust the start value.
  const int num_pixels = width * height;
  const int available_speed_levels = config.speed_levels.size();
  if (num_pixels > 1920 * 1080) {
    config.start_speed_index = std::max(available_speed_levels - 4, 0);
  } else if (num_pixels > 1280 * 720) {
    config.start_speed_index = std::max(available_speed_levels - 3, 0);
  } else if (num_pixels > 640 * 360) {
    config.start_speed_index = std::max(available_speed_levels - 2, 0);
  } else {
    config.start_speed_index = std::max(available_speed_levels - 1, 0);
  }

  return config;
}

}  // namespace webrtc
